#=============================================================================
# ■ Hermes - Hermes Extends RMXP's MEssage System - Version 0.2b
#-----------------------------------------------------------------------------
# End user edition
# This is the script for end users. Not recommended for developers.
# The configuration of Hermes starts below.
#=============================================================================

#=============================================================================
# ▼ CLASS Sprite_Face Begins
#=============================================================================
class Sprite_Face < Sprite
  # Here you can specify which "special commands" the \f tag allows.
  # E.g., \f[normal] wil be translated to \f[1], and so on.
  @@face_aliases = {"normal" => 1, "happy" => 2, "sad" => 3, "angry" => 4}

  def self.face_aliases
    # Returns an all face aliases in the form normal|happy|...|special|
    if @@face_aliases.empty?
      return ""
    else
      return @@face_aliases.keys.join("|") + "|"
    end
  end
end
#=============================================================================
# ▲ CLASS Sprite_Face Ends
#=============================================================================

#=============================================================================
# ▼ CLASS Hermes Begins
#=============================================================================
class Hermes < Window_Selectable
  #===========================================================================
  # ● Configuration section
  #===========================================================================
  module GlobalText                # Global Font / Style Configuration
    NAME    = Font.default_name      # Font name (case sensitive)
    SIZE    = Font.default_size      # Text size in pixel
    COLOR   = Font.default_color     # Text color (Color.new(red, green, blue))
    BOLD    = Font.default_bold      # Text boldness (true/false)
    ITALIC  = Font.default_italic    # Text italicness   -"-
    # The syntax of the following values is [strength(, color)?]
    OUTLINE = [0]                    # Text outline
    SHADOW  = [2]                    # Text shadow
  end

  # Set the button to skip the dialog
  #  B = Escape, 0 (On The NumPad), X
  #  C = Enter, Space Bar and C
  #  A = Shift, Z
  SKIP_TEXT_CODE = Input::C

  # The height in pixels of the face graphics in facesets
  FACE_HEIGHT = 128

  # Default Japanese font (for use with \kana tag)
  KANA_FONT_NAME = "MS PGothic"

  # Use backwards compatible tags. Possible values:
  # - "AMS+": tags backwards compatible to AMS and AMS+
  # - "Hermes0.2": tags backwards compatible to Hermes 0.1 to 0.2c
  # - nil: only compatible to Hermes >= 0.3 (default)
  TAG_VERSION = nil
  # Setting compatibility mode might reduce speed.

  module Message                 # Configuration for message box
    module Text                    # Configuration for the text it contains
      NAME   = "default"             # Default font's name (case sensitive)
      SIZE   = "default"             # Default text size in pt
      COLOR  = "default"             # Default text color (Color.new(r, g, b))
      BOLD   = "default"             # Default text boldness (true/false)
      ITALIC = "default"             # Default text italicness   -"-
      ALIGN  = 0                     # Default horizontal text align (0, 1 or 2)
      VALIGN = 0                     # Default vertical text align (0, 1, or 2)
      SOUND  = "off"                 # Default sound to play on each letter
      SPEED  = 1                     # Default speed for text display (0=∞)
      PREVENT_SKIPPING = false       # Default for text skipping prevention
      # The syntax of the following values is [strength(, color)?] or "default"
      OUTLINE = "default"            # Default text outline
      SHADOW  = "default"            # Default text shadow
    end
    module Box                     # Configuration for the box itself
      WIDTH   = 480                  # Width (yeah, really?)
      LINE_HEIGHT = 32               # And the height of each line
      MARGIN  = 16                   # Spacing from border
      EVENT_X = 0                    # X offset when shown over an event
      EVENT_Y = 48                   # Y offset when shown over an event
      OPACITY = 150                  # Default opacity (255 = fully opaque)
      SKIN    = "default"            # Default Windowskin
    end
  end

  module Name                    # Configuration for name box
    module Text                    # Configuration for the text it contains
      NAME   = "default"             # Default font's name (case sensitive)
      SIZE   = "default"             # Default text size in pt
      COLOR  = "default"             # Default text color (Color.new(r, g, b))
      BOLD   = "default"             # Default text boldness (true/false)
      ITALIC = "default"             # Default text italicness   -"-
      # The syntax of the following values is [strength(, color)?] or "default"
      OUTLINE = "default"            # Default text outline
      SHADOW  = "default"            # Default text shadow
    end
    module Box                     # Configuration for the box itself
      POS     = 0                    # Position: 0=top left,    1=top right,
                                     #           3=bottom left, 2=bottom right
      OVERLAP = 16            # Number of pixels overlapping the message box (y)
      OFFSET  = 8             # Number of pixels apart from the corner (x)
      PADDING = 4             # Number of pixels left and right of the content
      SWAP    = true          # Swap position when name box would leave screen
      OPACITY = 200                  # Default opacity (255 = fully opaque)
      SKIN    = "default"            # Default Windowskin
    end
  end

  # Character replacements ($A - $z)
  REPLACEMENTS = {
    "$" => "$", # Dollar sign
    "a" => "☺", "b" => "☻", "c" => "☹", # Happy, black happy (1), and sad faces
    "d" => "☁", "e" => "❄", # Cloud, snow flake (one / two water drops in RM2k)
    "f" => "♤", "g" => "♡", "h" => "♢", "i" => "♧", # As below, but not filled
    "j" => "♠", "k" => "♥", "l" => "♦", "m" => "♣", # Card game symbols
    # Skull, Christian cross, Sun, Moon (2), Star/Bullet
    "n" => "☠", "o" => "✟", "p" => "☀", "q" => "☾", "r" => "•",
    "s" => "⇧", "t" => "⇨", "u" => "⇩", "v" => "⇦", # North, East, South, West
    "w" => "⬀", "x" => "⬂", "y" => "⬃", "z" => "⬁", # NE, SE, SW and NW arrows
    "A" => "☮", "B" => "☢", "C" => "✡", # Peace, radioactive, Star of David (3)
    "D" => "☉", "E" => "☽", "F" => "☿", "G" => "♀", # Sun, Moon, Mercury, Venus
    "H" => "♁", "I" => "♂", "J" => "♃", "K" => "♄", # Earth,Mars,Jupiter,Saturn
    "L" => "♅", "M" => "♆", "N" => "♇", # Uranus, Neptune, Pluto
    "O" => "♈", "P" => "♉", "Q" => "♊", # Aries, Taurus, Gemini
    "R" => "♋", "S" => "♌", "T" => "♍", # Cancer, Leo, Virgo
    "U" => "♎", "V" => "♏", "W" => "♐", # Libra, Scorpio, Sagittarius
    "X" => "♑", "Y" => "♒", "Z" => "♓"  # Capricorn, Aquarius, Pisces
    # (1): This is a neutral face in RM2k.
    # (2): In RM2k, this is a filled half moon. Here it's a mirrored $E.
    # (3): Was Sword, Shield, Star of David in RM2k.
  }.freeze

  #===========================================================================
  # ● Tags section
  #===========================================================================
  # These classes and methods are required for the tags to get added.
  Tag = Struct.new("Tag", :long_name, :params, :live, :method)
  @@tags = {}; @@aliases = []
  def self.tag(name, long_name, params = nil, &block)
    method = "tag_#{long_name.downcase}".to_sym
    define_method(method, &block)
    @@tags[name] = Tag.new(long_name, params, false, method)
  end
  def self.live_tag(name, long_name, params = nil, &block)
    tag(name, long_name, params, &block).live = true
  end

  # Show face tag
  tag "f", "Face", /([^,]+)?(,(#{Sprite_Face.face_aliases}\d+))?/ do |m|
    if @face_sprite
      return false
    else
      if m[0]
        if m[2]
          face_id = m[3]
          face_id = face_id.to_i if face_id[/\A\d+\z/]
        else
          face_id = 0
        end
        id = (m[1] or $game_party.actors[0].id.to_s)
      else
        face_id = 0
        id = $game_party.actors[0].id
      end
      if id.kind_of? Integer or id[/\A\d+\z/]
        face_file = "face_" + id.to_s
      else
        face_file = id
      end
      @face_sprite = Sprite_Face.new(face_file, self.z + 1, face_id)
      @indent = 16 + @face_sprite.face_width
      ""
    end
  end
  # Display name in name box
  tag "n", "NameBox", /(.+)/ do |m|
    if @name_window
      return false
    else
      id = (m[1] or $game_party.actors[0].id.to_s)
      if id[/\A\d+\z/] and $game_actors[id.to_i]
        name_text = $game_actors[id.to_i].name
      else
        name_text = id
      end
      @name_window = Window_Name.new(name_text, self.z + 1)
      ""
    end
  end
  # Display name and face graphic
  tag "d", "Chara", @@tags["f"].params do |m|
    return (send(@@tags["f"].method, m)+
            send(@@tags["n"].method, m))
  end
  # Get Character ID from database, named after the famous airship guy
  tag "#", "MemberID", /[0-3]/ do |m|
    $game_party.actors[m[0] ? m[0].to_i : 0].id
  end
  # Show character name
  tag "a", "Actor", /\d+/ do |m|
    id = m[0] ? m[0].to_i : $game_party.actors[0].id
    $game_actors[id] ? $game_actors[id].name : false
  end
  # Show event name
  tag "e", "Event", /this|\d+/ do |m|
    if not m[0] or m[0] == "this"
      id = $game_system.map_interpreter.event_id
    else
      id = m[0].to_i
    end
    return $game_map.events[id] ? $game_map.events[id].name : false
  end
  # Show item pricing
  tag "q", "Price", /\d+/ do |m|
    id = m[0] ? m[0].to_i : -1
    $data_items[id] ? $data_items[id].price : false
  end
  # Show hero class
  tag "j", "Class", /\d+/ do |m|
    id = m[0] ? m[0].to_i : $game_party.actors[0].id
    if $data_classes[$data_actors[id].class_id]
      $data_classes[$data_actors[id].class_id].name
    else
      false
    end
  end
  # Show current map
  tag "m", "Map" do
    $game_map.name ? $game_map.name : ""
  end
  # Show variables and item, weapon, armor, skill names
  tag "v", "Variable", /([eiwas])?,?(\d+)/i do |m|
    item =
    if m[0]
      index = m[2].to_i
      arg = m[1]
      case arg
        when "e" then $data_enemies[index]
        when "i" then $data_items[index]
        when "w" then $data_weapons[index]
        when "a" then $data_armors[index]
        when "s" then $data_skills[index]
        else $game_variables[index]
      end
    end
    if not item
      false
    elsif arg == "e"
      item.name
    elsif arg
      add_command("y", [item.icon_name])
      @line_widths[@line] += RPG::Cache.icon(item.icon_name).width
      " #{item.name}"
    else
      item
    end
  end
  # Name box: Change text color
  tag "z", "NameColor", /\d|\#[\dA-F]{6}/i do |m|
    if @name_window
      @name_window.color =
        m[0] ? text_color(m[0]) : $msg.name_font.color.dup
      ""
    else
      return false
    end
  end
  # Show gold Window
  tag "g", "Gold" do
    if not @gold_window
      if @popchar != -1
        @gold_window = Window_Gold.new
        @gold_window.opacity = self.opacity
        @gold_window.back_opacity = self.back_opacity
      end
    else
      @gold_window.dispose
      @gold_window = nil if @gold_window.disposed?
    end
    return ""
  end
  # Text align
  tag "x", "Align",
      /(l(?:eft)?|c(?:enter)?|r(?:ight)?),?(t(?:op)?|m(?:iddle)?|b(?:ottom)?)/ do |m|
    if m[0]
      @align = case m[1][0,1]
        when 'l' then 0
        when 'c' then 1
        when 'r' then 2
      end
      @valign = case m[2][0,1]
        when 't' then 0
        when 'm' then 1
        when 'b' then 2
      end
    else
      @align = $msg.align
      @valign = $msg.valign
    end
    return ""
  end
  # Turn bold on or off
  live_tag "b", "Bold", /on|off/ do |m|
    self.contents.font.bold = m[0] ? m[0] == "on" : !self.contents.font.bold
  end
  # Turn italic on or off
  live_tag "i", "Italic", /on|off/ do |m|
    self.contents.font.italic = m[0] ? m[0] == "on" : !self.contents.font.italic
  end
  # Change font type
  live_tag "t", "Font", /.+/ do |m|
    self.contents.font.name = (m[0] or $msg.font.name)
  end
  # Change font color
  live_tag "c", "Color", /\d|\#[\dA-F]{6}/i do |m|
    self.contents.font.color = m[0] ? text_color(m[0]) : $msg.font.color.dup
  end
  # Change text speed
  live_tag "s", "Speed", /\d+/ do |m|
    # Don't change in width trial mode
    unless @status == :width_trial
      @write_speed = [[m[0] ? m[0].to_i : $msg.speed, 0].max, 20].min
    end
  end
  # Change text opacity
  live_tag "o", "Opacity", /\d{1,3}/ do |m|
    self.contents.font.color.alpha = m[0] ? m[0].to_i : $msg.font.color.alpha
  end
  # Change text size
  live_tag "h", "Size", /\d+/ do |m|
    size = m[0] ? m[0].to_i : $msg.font.size
    self.contents.font.size = [[size, 6].max, 32].min
  end
  # Prevent text skipping on / off
  live_tag "%", "PreventSkip", /on|off/ do |m|
    unless @status == :width_trial
      @prevent_skipping = m[0] ? (m[0] == "on") : !@prevent_skipping
    end
  end
  # Show icon
  live_tag "y", "Icon", /.+/ do |m|
    # Disable in width trial mode
    if @status == :width_trial
      @line_widths[@line] += 28
    else
      if m[0]
        icon = RPG::Cache.icon(m[0])
        self.contents.blt(@x , (@y + 0.5) * Hermes::Message::Box::LINE_HEIGHT +
                          @align_offset - icon.height / 2, icon, icon.rect)
        Audio.se_play("Audio/SE/" + @sound) if @sound
      end
      @x += icon.width
    end
  end
  # Change sound effect to be played on letter appearing
  live_tag "r", "Sound", /.+/ do |m| @sound = m[0] end
  # Wait 0.25 seconds
  live_tag ".", "Wait1" do @write_wait += 5 unless @status == :width_trial end
  # Wait one second
  live_tag "|", "Wait4" do @write_wait += 20 unless @status == :width_trial end
  # Wait for keypress
  live_tag "!", "Pause" do @status = :pause unless @status == :width_trial end
  # Close message window
  live_tag "^", "Close" do
    terminate unless skip_text or (@status == :width_trial)
  end

  #===========================================================================
  # ● Tag compatibility section
  #===========================================================================
  def self.add_alias(key, value)
    key = key.is_a?(Regexp) ? /\\#{key}/ : "\\#{key}"
    @@aliases << [key, "\\#{value}"]
  end
  def self.add_aliases(hash) hash.each { |key,value| add_alias(key, value) } end

  case TAG_VERSION
  when "AMS+" then
    add_aliases({"align" => "x", "chara" => "d", "class" => "j",
      /[Cc]id/ => "#", "map" => "\000m", "name" => "\000n", "icon" => "y"})
    add_alias(/a/i, "r")
    add_aliases({"B" => "b", "C" => "c", "F" => "f", "G" => "g", "H" => "h",
      "I" => "i", /m\[/i => "v[e", /n/i => "a", "O" => "o", "price" => "q",
      "S" => "s", "T" => "t", "V" => "v", "Z" => "z", ">" => "s", "<" => "s[0]",
      "~" => "^"})
    add_aliases({"\000m" => "m", "\000n" => "n"})
  when "Hermes0.2"
    add_aliases({/s(?:ound)?/i => "r", /i(?:con)?/i => "y"})
    add_aliases({/f(?:ace)?/i => "f", /n(?:ame)?/i => "n", /d(?:ata)?/i => "d",
      /c(?:id)?/i => "#", /a(?:ctor)?/i => "a", /e(?:vent)?/i => "e",
      /co(?:st)?/i => "q", /cl(?:ass)?/i => "j", /m(?:ap)?/i => "m",
      /v(?:ar)?/i => "v", /g(?:old)?/i => "g", /ta(?:lign)?/i => "x",
      /tb(?:old)?/i => "b", /ti(?:talic)?/i => "i", /tf(?:ont)?/i => "t",
      /tc(?:olor)?/i => "c", /ts(?:peed)?/i => "s", /to(?:pacity)?/i => "o",
      /th(?:eight)?/i => "h", "~" => "^"})
  end
end
#=============================================================================
# ▲ CLASS Hermes Ends
#=============================================================================

#=============================================================================
# ▼ CLASS Hermes_Global_Settings Begins
#=============================================================================
class Hermes_Global_Settings
  # Make the changeable options... changeable ;)
  attr_reader :font, :name_font

  def initialize
    @font = Font.new
    @name_font = Font.new
    ["", "name_"].each do |p|
      ["name","size","color","bold","italic","outline","shadow"].each do |n|
        send("#{p}font_#{n}=".to_sym, nil)
      end
      ["skin", "opacity"].each do |n|
        send("#{p}#{n}=".to_sym, nil)
      end
    end
    [:sound=, :align=, :valign=, :prevent_skipping=, :speed=].each do |p|
      send p, nil
    end
  end

  #--------------------------------------------------------------------------

  ["", "name_"].each do |p|
    # Add methods [name_]font_name[=], ..., [name_]font_shadow[=]
    ["name", "size", "color", "bold", "italic", "outline", "shadow"].each do |n|
      define_method("#{p}font_#{n}") do
        return instance_variable_get("@#{p}font").send("#{n}")
      end
      define_method("#{p}font_#{n}=") do |arg|
        # Gets the default if requested
        arg = get_value(arg ? arg : (Hermes.const_get((p == "") ? "Message" :
          "Name")::Text.const_get(n.upcase)), Font.send("default_#{n}"))
        if n == "outline" or n == "shadow"
          if arg.is_a? Array
            arg = TextEffect.new(*arg)
          elsif arg.is_a? TextEffect
            arg = arg.dup
          else
            raise TypeError, "Argument specified must be a TextEffect object, "+
                             "an Array, \"default\", or left out (nil)."
          end
        end
        return instance_variable_get("@#{p}font").send("#{n}=", arg)
      end
    end
    # Add methods [name_]skin[=], [name_]opacity[=]
    attr_reader("#{p}skin")
    define_method("#{p}skin=") do |arg|
      return instance_variable_set "@#{p}skin", get_value(arg ? arg :
        (Hermes.const_get((p == "") ? "Message" : "Name")::Box::SKIN),
        $game_system.windowskin_name)
    end
    # Add methods [name_]opacity[=]
    attr_reader("#{p}opacity")
    define_method("#{p}opacity=") do |arg|
      return instance_variable_set "@#{p}opacity", arg ? arg :
        (Hermes.const_get((p == "") ? "Message" : "Name")::Box::OPACITY)
    end
  end

  # Add methods [v]align[=]
  ["align", "valign"].each do |p|
    attr_reader(p)
    define_method("#{p}=") do |arg|
      unless arg and (0..2) === arg
        arg = Hermes::Message::Text.const_get(p.upcase)
      end
      return instance_variable_set "@#{p}", arg
    end
  end

  # Add methods prevent_skipping[=], speed[=]
  ["prevent_skipping", "speed"].each do |p|
    attr_reader(p)
    define_method("#{p}=") do |arg|
      return instance_variable_set "@#{p}",
        (arg or Hermes::Message::Text.const_get(p.upcase))
    end
  end

  # Add methods sound[=]
  attr_reader :sound
  def sound=(arg)
    unless arg
      arg = Hermes::Message::Text::SOUND
    end
    if arg == "off"
      @sound = nil
    else
      @sound = arg
    end
  end

  #--------------------------------------------------------------------------

  def get_value(constant, default)
    # Gets the default if requested
    if constant != "default"
      return constant
    else
      return default
    end
  end

  protected :get_value
end
#=============================================================================
# ▲ CLASS Hermes_Global_Settings Ends
#=============================================================================

#=============================================================================
# ▼ CLASS Hermes Begins
#=============================================================================
class Hermes
  Command = Struct.new("Command", :method, :params)
  Char = Struct.new("Char", :c, :commands)

  module Options
    Input_Number = Struct.new('Input_Number', :variable_id, :digits_max)
    Show_Choice = Struct.new('Show_Choice', :start, :max, :cancel_type, :proc)
  end

  def initialize(char, text, proc = nil, options = nil, init_opacity = 0)

    #-----------------------------------------------------
    # Initialize instance variables and window attributes
    #-----------------------------------------------------

    super(0, 0, 10, 10)
    self.contents = Bitmap.new(1, 1)
    self.visible = false
    self.z =
    # Make active unless it is an asyncronous message
    if self.active = (@proc = proc) != nil
      $game_temp.message_window_showing = true
      9998
    else
      9995
    end
    self.index = -1
    # Initialize variables
    self.contents.font = $msg.font.dup
    skin_file = $msg.skin
    if FileTest.exist?("Graphics/Pictures/" + skin_file)
      custom_skin = RPG::Cache.picture(skin_file)
    else
      custom_skin = nil
      self.windowskin = RPG::Cache.windowskin(skin_file)
    end
    skin_file = nil
    @sound = $msg.sound
    @prevent_skipping = $msg.prevent_skipping
    @write_speed = $msg.speed
    @align, @valign = $msg.align, $msg.valign
    @x = @y = @cursor_width = @choice_indent = @indent = 0
    @cursor_width = 0
    @write_wait = 0
    @popchar = char
    @character = case @popchar
      when 0 then $game_player
      when 1..$game_map.events.keys.max then $game_map.events[@popchar]
    end
    @align_offset = 0
    @cur_line = @scroll_break = 0
    @messages = []
    @line_widths = [0]
    @commands = []

    #--------------------------------
    # Get and parse the message text
    #--------------------------------

    # Get text (but remove whitespace)
    @now_text = text.strip

    # Before parsing, number the [] brackets in the message text, escaping the
    # "real" { characters
    @now_text.gsub!("{", "\\{")
    level = 0
    r = ""
    @now_text.gsub!(/(\[|\])/) do
      level -= 1 if $1 == "]"
      r = "#{$1}{#{level}}"
      level += 1 if $1 == "["
      r
    end
    r = level = nil

    # Replace compatibility aliases in the message
    @@aliases.each do |rule|
      @now_text.gsub!(*rule)
    end

    # Parse all tags and save output
    @status = :width_trial
    @now_text = parse_tags(@now_text)
    # This also sets: @line_widths
    @status = :typing

    #-------------------------------------------------
    # Initialize Show choice and Input number options
    #-------------------------------------------------

    # If display options where specified
    if options
      @options = options
      if @options.is_a?(Options::Input_Number)
        # Initiate Input Number Window
        @input_number_window = Window_InputNumber.new(@options.digits_max)
        @input_number_window.number = $game_variables[@options.variable_id]
        @input_number_window.contents_opacity = 0
        @input_number_window.visible = false
      elsif @options.is_a?(Options::Show_Choice)
        # Set choice start and calculate cursor width
        visible_line_count = @line_widths.size
        visible_line_count = [visible_line_count, 4].min if @popchar < 0
        @options.start = visible_line_count - (@item_max=@options.max)
        # Set choice width plus padding
        @cursor_width = @line_widths[@options.start..-1].max + 8
        # Don't show the cursor in the beginning
        self.index = -1
      end
    end

    #---------------------------------------------------------------------------
    # Initialize the window's size and induced default posotions of sub-windows
    #---------------------------------------------------------------------------

    # Set width and height of window
    lines = @now_text.split("\n", -1)
    # Get number of lines
    line_count = lines.size
    line_count += 1 if @options.is_a?(Options::Input_Number)
    if @popchar >= 0
      # Get maximal line width
      w = @line_widths.max
      if @input_number_window and @input_number_window.width > w
        # Adjust size when input number used
        w = @input_number_window.width
      end
      # Minimum width
      if w == 0
        w = 33
      else
        w += 40 + @indent
      end
      # Calculate window height
      h = line_count * Hermes::Message::Box::LINE_HEIGHT
      # Minimum height
      if h == 0
        h = 33
      else
        h += 32
      end
    else
      if @popchar == -1
        w, h = 640, 480
      else
        w = Hermes::Message::Box::WIDTH
        h = 4 * Hermes::Message::Box::LINE_HEIGHT + 32
      end
    end
    self.width, self.height = w, h

    # Apply vertical align if normal message with less than 4 lines or OSD
    if (@popchar == -2 and line_count < 4) or @popchar == -1
      th = h - (32 + Hermes::Message::Box::LINE_HEIGHT * line_count)
      @align_offset = (@valign * th) / 2
    end

    # Calculate choice indent
    if @options.is_a?(Options::Show_Choice)
      textarea_width = w - 32 - @indent
      @choice_indent = (@align * (textarea_width - @cursor_width)) / 2
    end

    # Set position of the face sprite if window is tall enough
    if @face_sprite and h - 32 >= @face_sprite.face_height
      @face_sprite.align = 1
    end

    # Create room to draw
    self.contents = Bitmap.new(w - 32, h - 32)
    self.contents.font = $msg.font.dup
    if @options.is_a?(Options::Input_Number)
      @input_number_window.contents.font = self.contents.font
    end

    #----------------------------------------
    # Set initial positions of movable parts
    #----------------------------------------

    reset_position
    # Sets self.x, self.y[, @name_window.x, @name_window.y][, @face_sprite.x,
    # @face_sprite.y[, @face_sprite.align][, @name_window.indent]]
    # [, @input_number_window.x, @input_number_window.y][, @gold_window.x,
    # @gold_window.y]

    #-------------------------
    # Complete initialization
    #-------------------------

    # Set opacities
    if @popchar == -1 or custom_skin
      self.opacity = 0
      self.back_opacity = 0
    elsif $game_system.message_frame == 0
      self.opacity = 255
      self.back_opacity = $msg.opacity
    else
      self.opacity = 0
      self.back_opacity = $msg.opacity
    end
    self.contents_opacity = init_opacity

    # Draw custom window skin
    if custom_skin and $game_system.message_frame != 0
      self.contents.stretch_blt(self.contents.rect, custom_skin,
                                custom_skin.rect, $msg.opacity)
    end

    # First x offset
    @x = @indent + align_indent

    # And finally, show
    Graphics.frame_reset
    self.visible = true
  end

  #--------------------------------------------------------------------------

  # Function to add a command that will be executed later
  def add_command(name, params, ahead = 0)
    # Don't add tags if not in actual parse mode
    if @line and @pos
      raise "Invalid tag name #{name}!" unless @@tags.include? name
      pos = @pos + ahead
      # Executed again later
      (@commands[pos] or @commands[pos] = []) <<
        Command.new(@@tags[name].method, params)
    end
  end

  #--------------------------------------------------------------------------

  # Parses all the tags in a message
  def parse_tags(string, internal = false)
    # Keep completely empty lines
    string.gsub!(/\n(?=\n|$)/, "\n\000")

    # This translates all tags specified in @@tags and extracts line widths
    result = ""
    replace = ""
    m = nil
    pos = line = 0
    tag_name, tag = nil
    tag_found = arg_found = ""
    while char = string.slice!(/./m)
      if char == "\\"
        if string[0,1] == "\\" or char == "{"
          # Escaping
          char = string.slice!(/./m)
        else
          # Found a tag, check which one it is
          tag = nil
          if @@tags.include?(tag_name = tag_found = string[0, 1])
            # Short tag found!
            tag = @@tags[tag_name]
          else
            # Scan tags for long tags
            @@tags.each do |name, check_tag|
              if tag_found = string[/\A#{check_tag.long_name}/]
                tag_name, tag = name, check_tag
                break
              end
            end
          end
          # If we found a valid tag
          if tag
            # Remove tag
            string.slice!(0, tag_found.length)
            # Find submitted params
            arg_found =
            if tag.params and string.slice!(/\A\[(\{\d+\})(.*?)\]\1/)
              $2
            end
            # If [] brackets are supplied (always replace if no argument found)
            replace =
            if arg_found
              # Parse tags in the argument
              arg_found = parse_tags(arg_found, true)
              # Match the result against the params RegExp
              (m = arg_found.match(/\A#{tag.params}\z/).to_a).length != 0
            else
              m = []
              true
            end
            if replace
              # Save the line and pos so that tags can call add_command
              @line, @pos = line, pos unless internal
              replace = send(tag.method, m)
              if tag.live
                # Delete only
                replace = ""
                # Will again be executed later
                add_command(tag_name, m) unless internal
              end
              # Don't print the backslash
              char = nil
            end
            # If tag method returns false, don't replace the tag.
            replace = replace ? replace.to_s :
                      tag_found + (arg_found ? "[#{arg_found}]" : "")
            string.insert(0, replace)
          end
        end
      elsif char == "\n"
        # Delete empty lines
        if result == "" or string[0,1] == "\n" or result[-1,1] == "\n"
          char = nil
        else
          line += 1
          @line_widths[line] = 0 unless internal
        end
      elsif char == "$"
        # Set correct replacement from replacement table
        char = string.slice!(/./m)
        if REPLACEMENTS.include?(char)
          char = REPLACEMENTS[char]
        else
          char = "$#{char}"
        end
      elsif char == "{"
        # Delete bracket depth counter
        string.slice!(/\d+\}/)
        char = nil
      end
      if char
        result += char
        unless char == "\000" # Ignore Nullbytes
          pos += char.u_length
          unless internal
            @line_widths[line] += self.contents.text_size(char).width
          end
        end
      end
    end

    # Remove Nullbytes
    result.gsub!("\000", "")

    # Remove last character if it is a newline
    if not internal and result.sub!(/\n\z/, "")
      # Also remove last entry from @line_widths
      @line_widths.pop
    end

    return result
  end

  #--------------------------------------------------------------------------

  def contents_opacity=(o)
    super
    # Also set opacities of sub-windows
    @input_number_window.contents_opacity = o if @input_number_window
    @name_window.contents_opacity = o if @name_window
    @gold_window.contents_opacity = o if @gold_window
    @face_sprite.opacity = o if @face_sprite
    o
  end

  #--------------------------------------------------------------------------

  def opacity=(o)
    super
    # Also set opacities of sub-windows
    @input_number_window.contents_opacity = o if @input_number_window
    @name_window.opacity = o if @name_window
    @gold_window.opacity = o if @gold_window
    o
  end

  #--------------------------------------------------------------------------

  def update
    super()

    # Move Message Box with event
    if @character and (@character.moving? or (!self.active and $game_player.moving?))
      reset_position
    end

    # Perform Fade-in
    self.contents_opacity += 24 if self.contents_opacity < 255

    case @status
    when :typing
      # Decrease frames to wait
      @write_wait -= 1 if @write_wait > 0
      # Wait until next frame unless we've waited long enough or are skipping
      return unless @write_wait == 0 or skip_text

      while @now_text != "" or not @commands.empty?

        # Execute the commands one by one
        if @commands[0]
          while command = @commands[0].shift
            # Execute it
            send(command.method, command.params)
            # Exit if we have to wait or status has changed
            return unless @status == :typing and (@write_wait == 0 or skip_text)
          end
        end

        # Delete first command list (because it is empty! (or should be^^))
        @commands.shift

        if c = @now_text.slice!(/./m)
          if c == "\n"
            # New line
            @cur_line += 1
            @x = @indent + align_indent
            if @popchar > -2 or @cur_line < 4
              @y += 1
            else
              # Duplicate to blt it later
              @oldcontents = self.contents.dup
              @scroll_break = (@scroll_break + 1).modulo 4
              if @scroll_break == 1
                @status = :pause
                break
              end
            end
          else
            # Move old three lines to top
            if @oldcontents
              self.contents.clear
              self.contents.blt(0, -Hermes::Message::Box::LINE_HEIGHT,
                @oldcontents, @oldcontents.rect)
              @oldcontents.dispose
              @oldcontents = nil
            end
            # We have a letter! Now let's draw!
            self.contents.draw_text(@x, @y * Hermes::Message::Box::LINE_HEIGHT +
              @align_offset, 40, Hermes::Message::Box::LINE_HEIGHT, c)
            @x += self.contents.text_size( c ).width
            # Don't play a sound during instant display
            if @sound and @write_speed > 0
              Audio.se_play("Audio/SE/" + @sound)
            end
          end
        end

        # Continue with next letter if skipping
        if skip_text or @write_speed == 0
          next
        else
          # Stretch time to wait by global speed factor and add own wait time
          @write_wait = (@write_wait + 1) * @write_speed
          return
        end
      end

      # Switch to done status when text is fully drawn
      if @now_text == "" and @commands.empty? and
         (@write_wait == 0 or skip_text)
        if @options.is_a?(Options::Show_Choice)
          # Also, show the choice cursor
          @index = 0
        elsif @options.is_a?(Options::Input_Number)
          # ...or the input number window
          @input_number_window.contents.font = self.contents.font
          @input_number_window.refresh
          @input_number_window.visible = true
        end
        # Show continue cursor if applicable
        self.pause = (@options == nil and self.active)
        # Set status to done if fully faded in
        @status = :done
      end
    when :pause
      # Switch to pause mode if not yet in it
      if self.active and not self.pause
        self.pause = true
        @write_wait = 0
      end

      if Input.trigger?(Input::C)
        # Enter key pressed: resume dialog
        if @status == :pause
          @status = :typing
          self.pause = false
          return
        else
          self.terminate
        end
      end
    when :done
      # Pass control to input number window
      if @options.is_a?(Options::Input_Number)
        # Update input number window
        @input_number_window.update
        if Input.trigger?(Input::C)
          # Enter key pressed
          $game_system.se_play($data_system.decision_se)
          $game_variables[@options.variable_id] =
          @input_number_window.number
          $game_map.need_refresh = true
          self.terminate
        end
      elsif @options.is_a?(Options::Show_Choice)
        # Choice is active
        if Input.trigger?(Input::B) and
          # Escape key pressed
          if @options.cancel_type > 0
            $game_system.se_play($data_system.cancel_se)
            @options.proc.call(@options.cancel_type - 1)
            self.terminate
          end
        end
        if Input.trigger?(Input::C)
          # Enter key pressed
          $game_system.se_play($data_system.decision_se)
          @options.proc.call(self.index)
          self.terminate
        end
      elsif self.active
        # Enter key pressed: close dialog
        self.terminate if Input.trigger?(Input::C)
      end
    when :closing
      # Start fading out
      self.opacity -= 48
      self.contents_opacity -= 48
      self.dispose if self.contents_opacity == 0
    end
  end

  #--------------------------------------------------------------------------

  def update_cursor_rect
    # Update rect used for cursor display
    if @index >= 0
      self.cursor_rect.set(@indent + @choice_indent, (@options.start + @index) *
        Hermes::Message::Box::LINE_HEIGHT + @align_offset, @cursor_width,
        Hermes::Message::Box::LINE_HEIGHT)
    else
      self.cursor_rect.empty
    end
  end

  #--------------------------------------------------------------------------

  def align_indent
    text_width = @line_widths[@cur_line] ? @line_widths[@cur_line] : 0
    # Calculate size of space for drawing text
    textarea_width =
    if not @options.is_a?(Options::Show_Choice) or @cur_line < @options.start
      self.contents.width - @indent
    else
      # Writing the choices
      @cursor_width
    end
    indent = 4 + (@align * (textarea_width - text_width - 8)) / 2
    if @options.is_a?(Options::Show_Choice) and @cur_line >= @options.start
      indent += @choice_indent
    end
    return indent
  end

  #--------------------------------------------------------------------------

  # Determine wether to show the text at once
  def skip_text
    # - We're NOT in pause mode and:
    #   - Allowed to skip and pressing enter or
    #   - In debug mode and control held and enter pressed
    skip_key_trigger = Input.trigger?(Hermes::SKIP_TEXT_CODE)
    @status != :pause and (
      (self.active and not @prevent_skipping and skip_key_trigger) or
      ($DEBUG and Input.press?(Input::CTRL) and skip_key_trigger)
    )
  end

  #--------------------------------------------------------------------------

  def reset_position
    # Reset width, height and position of the message window
    if @popchar >= 0
      # This updates the message position if popchar is moving
      # Get an event or player
      if @character
        # Calculate new position
        x = @character.screen_x - Hermes::Message::Box::EVENT_X - self.width / 2
        y = @character.screen_y - Hermes::Message::Box::EVENT_Y - self.height
        # Restrict to screen unless asynchronous
        if @proc
          x = [[x, 4].max, 636 - self.width].min
          y = [[y, 4].max, 476 - self.height].min
        end
        # Adjust position if we have a name window
        if @name_window and not Hermes::Name::Box::SWAP
          name_offset = @name_window.height - Hermes::Name::Box::OVERLAP
          if Hermes::Name::Box::POS <= 1
            y = name_offset + 4 if y < name_offset + 4
          elsif y > 476 - self.height - name_offset
            y = 476 - self.height - name_offset
          end
          name_offset = @name_window.width+Hermes::Name::Box::OFFSET-self.width
          if (1..2) === Hermes::Name::Box::POS
            x = name_offset + 4 if x < name_offset + 4
          elsif x > 636 - self.width - name_offset
            x = 636 - self.width - name_offset
          end
        end
      end
    elsif @popchar == -1
      # Display across the whole screen
      x = y = 0
    else
      # Normal display
      if $game_temp.in_battle
        y = 16
      else
        case $game_system.message_position
        when 0
          y = Hermes::Message::Box::MARGIN
        when 1
          y = 240 - (self.height / 2)
        when 2
          y = 480 - Hermes::Message::Box::MARGIN - self.height
        end
      end
      x = 320 - (self.width / 2)
    end
    # Set new position
    self.x, self.y = x, y
    rect = Rect.new(x, y, self.width, self.height)
    # Update Name Window position
    if @name_window
      pos = @name_window.reset_position(rect)
    end
    # Update Face Sprite position
    if @face_sprite
      condpos = @face_sprite.reset_position(rect)
      # Update Name Window pos
      if @name_window
        if pos == condpos
          @name_window.indent = @face_sprite.face_width + 16
        else
          @name_window.indent = 0
        end
      end
    end
    # Update Input Number Window position
    if @options.is_a?(Options::Input_Number)
      textarea_width = self.contents.width - @indent
      x = (@align * (textarea_width - @input_number_window.width + 32)) / 2
      @input_number_window.x = self.x + x
      @input_number_window.y = self.y + @align_offset +
        @line_widths.length * Hermes::Message::Box::LINE_HEIGHT
    end
    # Update Gold Window position
    if @gold_window
      if $game_temp.in_battle or self.x + (self.width / 2) <= 320
        @gold_window.x = 560 - @gold_window.width
      else
        @gold_window.x = 80
      end
      if $game_temp.in_battle
        @gold_window.y = 192
      else
        @gold_window.y = (self.y + (self.height / 2) <= 240) ? 384 : 32
      end
    end
  end

  #--------------------------------------------------------------------------

  def terminate
    self.active = false
    self.pause = false
    self.index = -1

    # Continue with event execution
    if @proc
      $game_temp.message_window_showing = false
      @proc.call
    end

    # Set status to closing
    @status = :closing
  end

  #--------------------------------------------------------------------------

  def dispose
    # Kill message
    self.terminate unless @status == :closing
    self.contents.dispose

    # Kill input number window
    @input_number_window.dispose if @input_number_window
    @name_window.dispose if @name_window
    @face_sprite.dispose if @face_sprite
    @gold_window.dispose if @gold_window
    super
  end
end
#=============================================================================
# ▲ CLASS Hermes Ends
#=============================================================================



#=============================================================================
# ● FEATURE Message Window integration, part 1
#=============================================================================

#=============================================================================
# ▼ CLASS Window_Message Begins
#=============================================================================
Window_Message = Class.new

class Window_Message < Object
  def initialize
    @combined_text = ""
    @messages = {}
    @sync_id = nil
  end

  # Add unused methods in case some script calls them
  def terminate_message
    @messages[@sync_id].terminate if @sync_id
  end

  def refresh() end

  def reset_window() end

  def update_cursor_rect
    @messages[@sync_id].update_cursor_rect if @sync_id
  end

  # Spawn a new message
  def spawn(message_text)
    message_text.strip!
    # Find any occurence of the \p tag
    if message_text.sub!(
       /\\(?:p|Popup)(?i-mx:\[(screen|this|hero|-1|\d+)?(,async)?\])?/, "")
      char = case $1
        when "screen" then -1
        when "this", nil then $game_system.map_interpreter.event_id
        when "hero" then 0
        else $1.to_i
      end
      async = ($2 != nil)
    else
      char, async = -2, false
    end
    # Load Input Number or Choice settings
    options =
    if $game_temp.num_input_variable_id > 0
      Hermes::Options::Input_Number.new($game_temp.num_input_variable_id,
        $game_temp.num_input_digits_max)
    elsif $game_temp.choice_max > 0
      Hermes::Options::Show_Choice.new(0, $game_temp.choice_max,
        $game_temp.choice_cancel_type, $game_temp.choice_proc)
    end
    if message_text.empty? and not options
      # Terminate asyncronous message if no text was submitted. This enables
      # the developer to manually close asynchronous messages.
      @messages[char].terminate if @messages[char]
      $game_temp.message_proc.call
    else
      # Set init_op to opacity of current message (canceling it) or else, 0
      if msg = @messages[char] and not msg.disposed?
        init_opacity = [msg.contents_opacity, msg.opacity].min
        msg.dispose
      else
        init_opacity = 0
      end
      @messages[char] = Hermes.new(char, message_text,
        (async ? nil : $game_temp.message_proc), options, init_opacity)
      if async
        # Proceed with event
        $game_temp.message_proc.call
      else
        # Memorize this message
        @sync_id = char
      end
    end
    clear_temp_vars
    $game_temp.message_text = nil
  end

  def update
    # If a transition has been started
    if $game_temp.transition_processing
      # Delete Popup message over player because he's most likely moving
      if @messages.include?(0)
        @messages[0].dispose if @messages[0]
        @messages.delete(0)
      end
      # Move all messages to the new place
      @messages.each_value do |msg|
        msg.reset_position if msg and not msg.disposed?
      end
      return
    end
    # Non-asynchronous message has ended and close is requested
    if @sync_id and not @messages[@sync_id].active
      @sync_id = nil
    end
    # Insert a new message if requested
    if $game_temp.message_text
      spawn($game_temp.message_text)
      $game_temp.message_text = nil
    end
    # Update asynchronous messages
    @messages.each do |char, msg|
      if msg.disposed?
        @messages.delete(char)
      else
        msg.update
      end
    end
  end

  def clear_temp_vars
    # Clear variables related to text, choices, and number input
    $game_temp.message_text = nil
    $game_temp.message_proc = nil
    $game_temp.choice_start = 99
    $game_temp.choice_max = 0
    $game_temp.choice_cancel_type = 0
    $game_temp.choice_proc = nil
    $game_temp.num_input_start = 99
    $game_temp.num_input_variable_id = 0
    $game_temp.num_input_digits_max = 0
  end

  def dispose
    @messages.each_value do |message|
      message.dispose
    end
  end
end
#=============================================================================
# ▲ CLASS Window_Message Ends
#=============================================================================



#=============================================================================
# ● FEATURE Combine messages and popup over current event
#=============================================================================

#=============================================================================
# ▼ CLASS Interpreter Additional Code Begins
#=============================================================================
class Interpreter
  #--------------------------------------------------------------------------
  # * Show Text
  #--------------------------------------------------------------------------
  def command_101
    # Set message text on first line
    @message_text = "" unless @message_text
    @message_text += @list[@index].parameters[0] + "\n"
    # Loop
    loop do
      # If next event command text is on the second line or after OR
      # current message contains a \* tag and next command is Show Text
      if @list[@index+1].code == 401
        # Add the second line or after to message_text
        @message_text += @list[@index+1].parameters[0] + "\n"
      else
        break
      end
      # Advance index
      @index += 1
    end
    # Continue only if message text contains \+ tag
    continue = @message_text.sub!(/\\\*/,"")
    unless continue
      # Set message end waiting flag and callback
      @message_waiting = true
      $game_temp.message_proc = Proc.new { @message_waiting = false }
      # Show message
      $scene.show_text(@message_text)
      @message_text = nil
    end
    continue
  end
  #--------------------------------------------------------------------------
  # * Show Choices
  #--------------------------------------------------------------------------
  def command_102
    # Set message end waiting flag and callback
    @message_waiting = true
    $game_temp.message_proc = Proc.new { @message_waiting = false }
    # Choices setup
    @message_text = "" unless @message_text
    # Set choice item count to choice_max
    $game_temp.choice_max = @parameters[0].size
    # Set choice to message_text
    for text in @parameters[0]
      @message_text += text + "\n"
    end
    # Set cancel processing
    $game_temp.choice_cancel_type = @parameters[1]
    # Set callback
    current_indent = @list[@index].indent
    $game_temp.choice_proc = Proc.new { |n| @branch[current_indent] = n }
    # Show message and choices
    $scene.show_text(@message_text)
    @message_text = nil
    # Continue
    return true
  end
  #--------------------------------------------------------------------------
  # * Input Number
  #--------------------------------------------------------------------------
  def command_103
    # Set message end waiting flag and callback
    @message_waiting = true
    $game_temp.message_proc = Proc.new { @message_waiting = false }
    # Number input setup
    $game_temp.num_input_variable_id = @parameters[0]
    $game_temp.num_input_digits_max = @parameters[1]
    # Show message and input number
    $scene.show_text((@message_text or ""))
    @message_text = nil
    # Continue
    return true
  end

  def event_id
    if @list and $game_map.events[@event_id].trigger <= 2
      @event_id
    else
      0
    end
  end
end
#=============================================================================
# ▲ CLASS Interpreter Additional Code Ends
#=============================================================================

# Class modifications which must only be defined once and new classes which
# don't have to be redefined.
unless defined? Hermes::MODIFIED
  class Hermes
    MODIFIED = true
  end

  #=============================================================================
  # ● FEATURE Global settings saving & loading
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Scene_Title Additional Code Begins
  #=============================================================================
  class Scene_Title
    alias old_command_new_game command_new_game
    def command_new_game
      $msg = Hermes_Global_Settings.new
      old_command_new_game
    end

    alias old_battle_test battle_test
    def battle_test
      old_battle_test
      $msg = Hermes_Global_Settings.new
    end
  end
  #=============================================================================
  # ▲ CLASS Scene_Title Additional Code Ends
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Scene_Save Additional Code Begins
  #=============================================================================
  class Scene_Save < Scene_File
    alias old_write_save_data write_save_data

    def write_save_data(file)
      old_write_save_data(file)
      Marshal.dump($msg, file)
    end
  end
  #=============================================================================
  # ▲ CLASS Scene_Save Additional Code Ends
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Scene_Load Additional Code Begins
  #=============================================================================
  class Scene_Load < Scene_File
    alias old_read_save_data read_save_data

    def read_save_data(file)
      old_read_save_data(file)
      $msg = Marshal.load(file)
    end
  end
  #=============================================================================
  # ▲ CLASS Scene_Load Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Message Window integration, part 2
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Scene_Map Additional Code Begins
  #=============================================================================
  class Scene_Map
    alias old_transfer_player transfer_player
    def transfer_player
      @message_window.update
      old_transfer_player
    end

    def show_text(text)
      @message_window.spawn(text)
    end
  end
  #=============================================================================
  # ▲ CLASS Scene_Map Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Unicode version for String#length
  #=============================================================================

  #=============================================================================
  # ▼ CLASS String Additional Code Begins
  #=============================================================================
  class String
    def u_length
      return self.dup.gsub(/./m, " ").length
    end
  end
  #=============================================================================
  # ▲ CLASS String Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Outline / Shadow
  #=============================================================================

  #=============================================================================
  # ▼ CLASS TextEffect Begins
  #=============================================================================
  class TextEffect
    attr_accessor :active, :size, :color
    def initialize(size = 1, color = Color.new(0, 0, 0, 128))
      @size = size.to_i
      @color = color
      @active = (@size > 0)
    end

    #--------------------------------------------------------------------------

    def active?
      return (@active and @size > 0)
    end

    #------------------------------------------------------------------------

    # Dup should also dup the effects
    def dup
      duplicate = super
      duplicate.color = @color.dup
      duplicate
    end
  end
  #=============================================================================
  # ▲ CLASS TextEffect Ends
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Font Additional Code Begins
  #=============================================================================
  class Font
    attr_accessor :outline, :shadow

    self.default_name = Hermes::GlobalText::NAME
    self.default_size = Hermes::GlobalText::SIZE
    self.default_color = Hermes::GlobalText::COLOR
    self.default_bold = Hermes::GlobalText::BOLD
    self.default_italic = Hermes::GlobalText::ITALIC
    @@default_outline = TextEffect.new(*Hermes::GlobalText::OUTLINE)
    @@default_shadow = TextEffect.new(*Hermes::GlobalText::SHADOW)

    def self.default_outline() @@default_outline  end
    def self.default_shadow()  @@default_shadow   end
    def self.default_outline=(outline) @@default_outline = outline end
    def self.default_shadow=(shadow)   @@default_shadow  = shadow  end

    alias old_font_init initialize

    def initialize(*args)
      old_font_init(*args)
      @outline = @@default_outline.dup
      @shadow  = @@default_shadow.dup
    end

    #------------------------------------------------------------------------

    # Code by KD - Make sure fonts can be saved and loaded
    def marshal_dump(*args)
      hash = Hash.new
      instance_variables.each do |sym|
        hash[sym.to_s[1..-1].to_sym] = instance_variable_get(sym)
      end
      [:name, :size, :color, :bold, :italic].each do |sym|
        hash[sym] = send(sym)
      end
      hash
    end

    def marshal_load(hash)
      initialize()
      hash.each do |sym, value|
        send("#{sym}=", value)
      end
    end

    #------------------------------------------------------------------------

    # Dup should also dup the effects
    alias old_dup dup
    def dup
      duplicate = old_dup
      duplicate.outline = @outline.dup
      duplicate.shadow = @shadow.dup
      duplicate
    end
  end
  #=============================================================================
  # ▲ CLASS Font Additional Code Ends
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Bitmap Additional Code Begins
  #=============================================================================
  class Bitmap
    alias old_bitmap_dt draw_text
    def draw_text(x, y, *args)
      return draw_text(x.x, x.y, x.width, x.height, y, *args) if x.is_a? Rect
      oldcolor = font.color.dup
      if font.shadow.active?
        font.color = font.shadow.color
        size = font.shadow.size
        old_bitmap_dt(x + size, y + size, *args)
      end
      if font.outline.active?
        font.color = font.outline.color
        size = font.outline.size
        range_x = (x - size)..(x + size)
        range_y = (y - size)..(y + size)
        font.color.alpha *= size / ((range_x.last - range_x.first) *
                                    (range_y.last - range_y.first) - 1).to_f
        range_x.each do |now_x|
          range_y.each do |now_y|
            unless x == now_x and y == now_y
              old_bitmap_dt(now_x, now_y, *args)
            end
          end
        end
      end
      font.color = oldcolor
      old_bitmap_dt(x, y, *args)
    end

    #------------------------------------------------------------------------

    # Code by KD - Copy special font properties when setting Bitmap font
    alias set_font font=
    def font=(newfont)
      set_font(newfont)
      newfont.instance_variables.each do |sym|
        font.instance_variable_set(sym, newfont.instance_variable_get(sym))
      end
    end
  end
  #=============================================================================
  # ▲ CLASS Bitmap Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Avatar Display
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Sprite_Face Begins
  #=============================================================================
  class Sprite_Face < Sprite
    # Faceset graphic, X / Y, Face ID, whether pinned on top instead of bottom
    def initialize(graphic, z, id = 0)
      # Initialize sprite
      super()

      # Load graphic
      self.bitmap = RPG::Cache.picture(graphic)

      # If single face graphic loaded
      if id == 0
        # Show complete bitmap
        self.src_rect = self.bitmap.rect
      # If face set loaded
      else
        # Special / invalid face IDs
        unless id.kind_of? Integer
          id = (@@face_aliases[id] or 1)
        end
        # Set source rect
        src_y, src_x = *(id.to_i - 1).divmod(4)
        face_width = self.bitmap.width / 4
        src_x *= face_width
        src_y *= Hermes::FACE_HEIGHT
        self.src_rect.set(src_x, src_y, face_width, Hermes::FACE_HEIGHT)
      end
      # Set z order
      self.z = z
      # Make transparent
      self.opacity = 0
    end

    #--------------------------------------------------------------------------

    def reset_position(parent)
      self.x = parent.x + 16
      # If window is tall enough
      if @align == 1
        self.y = parent.y + parent.height/2
        return nil
      else
        # Window is not tall enough
        if parent.y + parent.height - 32 >= self.face_height
          # Face can be shown aligned to bottom
          self.align = 2
          # Message is not displayed on top and not in full screen
          self.y = parent.y + parent.height - 16
          return 0
        else
          # Face must be aligned on top of the message
          self.align = 0
          # Message is displayed on top or in full screen
          self.y = parent.y + 16
          return 3
        end
      end
    end

    #--------------------------------------------------------------------------

    def face_width
      # Return the selected face's width
      return self.src_rect.width
    end

    #--------------------------------------------------------------------------

    def face_height
      # Return the selected face's height
      return self.src_rect.height
    end

    #--------------------------------------------------------------------------

    attr_reader :align
    def align=(align)
      # Set pin position
      align = 1 unless (0..2) === align
      self.oy = (align * self.src_rect.height) / 2
      @align = align
    end

    # Hide some methods
    protected :ox=, :oy=
  end
  #=============================================================================
  # ▲ CLASS Sprite_Face Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Map Name Display
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Game_Map Additional Code Begins
  #=============================================================================
  class Game_Map
    alias old_initialize initialize
    def initialize
      @map_names = load_data("Data/MapInfos.rxdata")
      @map_names.each do |key, info|
        @map_names[key] = info.name
      end
      old_initialize
    end

    def name
      @map_names[@map_id]
    end
  end
  #=============================================================================
  # ▲ CLASS Game_Map Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Event Name Display
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Game_Event Additional Code Begins
  #=============================================================================
  class Game_Event
    def name
      return @event.name
    end
  end
  #=============================================================================
  # ▲ CLASS Game_Event Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Hex Color Definition
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Window_Base Additional Code Begins
  #=============================================================================
  class Window_Base
    alias old_text_color text_color
    def text_color(color)
      if color.is_a? String and color[/\A\#[0-9A-F]{6}\z/i]
        # Valid hex color, create it
        return Color.new(color[1,2].hex, color[3,2].hex, color[5,2].hex,
          self.contents.font.color.alpha)
      else
        # Create of of the predefined colors
        return old_text_color(color.to_i)
      end
    end
  end
  #=============================================================================
  # ▲ CLASS Window_Base Additional Code Ends
  #=============================================================================



  #=============================================================================
  # ● FEATURE Name Window
  #=============================================================================

  #=============================================================================
  # ▼ CLASS Window_Name Begins
  #=============================================================================
  class Window_Name < Window_Base
    attr_reader :indent

    # Class for the Name Window
    def initialize(name, z)
      @indent = -1
      super(0, 0, 33, 33)
      self.windowskin = RPG::Cache.windowskin($msg.name_skin)
      self.back_opacity = $msg.name_opacity
      self.contents = Bitmap.new(1, 1)
      self.contents.font = $msg.name_font
      size = self.contents.text_size(name)
      w = 8 + size.width + Hermes::Name::Box::PADDING * 2
      self.width = w if w > 33
      h = 8 + size.height
      self.height = h if h > 33
      self.z = z
      @text = Sprite.new
      @text.bitmap = Bitmap.new(size.width, size.height)
      @text.bitmap.font = $msg.name_font
      self.contents.dispose
      @text.src_rect = @text.bitmap.rect
      @text.z = z + 1
      @text.opacity = 0
      # Draw the name
      @text.bitmap.draw_text(size, @name = name)
    end

    #--------------------------------------------------------------------------

    def apply_pos(parent, pos)
      return parent.x +
          if pos == 1 or pos == 2 # right
            parent.width - self.width - Hermes::Name::Box::OFFSET
          else                    # left
            Hermes::Name::Box::OFFSET
          end,
        parent.y +
          if pos < 2              # top
            Hermes::Name::Box::OVERLAP - self.height
          else                    # bottom
            parent.height - Hermes::Name::Box::OVERLAP
          end
    end

    #--------------------------------------------------------------------------

    def reset_position(parent)
      pos = Hermes::Name::Box::POS
      x, y = *apply_pos(parent, pos)
      # Swap if exiting the screen
      if (pos == 1 or pos == 2) and Hermes::Name::Box::SWAP and x < 0
        pos = (pos-1) * 3
      elsif Hermes::Name::Box::SWAP and x + self.width > 640
        pos = (pos-1).abs
      end
      if pos < 2 and Hermes::Name::Box::SWAP and y < 0
        pos = 3 - pos
      elsif Hermes::Name::Box::SWAP and y + self.height > 480
        pos = (pos - 3).abs
      end
      x, y = *apply_pos(parent, pos) unless pos == Hermes::Name::Box::POS
      self.x, self.y = x, y
      return pos
    end

    #--------------------------------------------------------------------------

    alias real_x= x=
    protected :real_x=
    def x=(x)
      super(@indent + x)
      @text.x = @indent + x + 4 + Hermes::Name::Box::PADDING if @text
    end

    #--------------------------------------------------------------------------

    def y=(y)
      super(y)
      @text.y = y + 4 if @text
    end

    #--------------------------------------------------------------------------

    def color=(c)
      @text.bitmap.font.color = c
      @text.bitmap.draw_text(@text.bitmap.rect, @name)
    end

    #--------------------------------------------------------------------------

    def opacity=(o)
      super(o)
      @text.opacity = o
    end

    #--------------------------------------------------------------------------

    def indent=(indent)
      self.real_x = self.x + indent - @indent
      @text.x += indent - @indent
      @indent = indent
    end

    #--------------------------------------------------------------------------

    def dispose
      @text.dispose
      super
    end

    #--------------------------------------------------------------------------

    def contents_opacity() @text.opacity end
    def contents_opacity=(opac) @text.opacity = opac end
  end
  #=============================================================================
  # ▲ CLASS Window_Name Ends
  #=============================================================================
end

Hermes.tag "k", "Kana", /.*/ do |m|
  # Test if a kana tag is open. Of course, this isn't a standard variable.
  unless @kana_font_before
    # m is an array which contains all the arguments like specified in the
    # regular expressions above. m[0] is the complete match from the RegExp
    # above, m[1] would be the first submatch and so on. If no argument was
    # submitted, m is an empty array.
    result = m[0]
    # If an argument was specified
    if result
      # Ensures the font used for Kana display is present on the target system.
      # Of course, kana_font_name isn't a default value of $msg, it will be
      # added below.
      if Font.exist?($msg.kana_font_name)
        # The following is needed so that we remember to reset the font to
        # the previous font name.
        @kana_font_before = self.contents.font.name
        # Add a command to change the font. This is needed, the kana tag is not
        # a live tag.
        add_command("t", [$msg.kana_font_name])
        # Set the font in width trial mode (in "live" mode, this is done by the
        # type command added above)
        self.contents.font.name = $msg.kana_font_name
        # Convert the argument to kana (the to_kana method is specified below)
        result = result.to_kana
        # Now add a command that will change the font back to normal. The last
        # argument ensures that it will not be added at the current position,
        # but result.u_length characters ahead (see also Misc section)
        add_command("t", [@kana_font_before], result.u_length)
        # And add another kana tag that will change the font back to normal
        # while in width trial mode (see the else clause below)
        result += "\\k"
      end
    else
      # No argument specified (result is nil): set it to empty string
      result = ""
    end
    return result
  else
    # This section is reached when a kana string has finished
    # Reset the font to normal
    self.contents.font.name = @kana_font_before
    # Clear the font before thing
    @kana_font_before = nil
    # Return an empty string so the tag gets deleted
    return ""
  end
end

# Add a compatibility tag if specified in the configuration:
if Hermes::TAG_VERSION == "Hermes0.2"
  Hermes.add_alias(/k(?:ana)?/i, "k")
end

class Hermes_Global_Settings # The class name of $msg.
  # Make @kana_font_name readable
  attr_reader :kana_font_name

  # This will make $msg.kana_font_name changeable. Note that the convention is,
  # when you call this function with nil as an argument, it will ne reset to the
  # default value (see above).
  def kana_font_name=(arg)
    return @kana_font_name = (arg or Hermes::KANA_FONT_NAME)
  end

  # Finally, assign the default value on creation of the $msg class. You need to
  # overwrite the initialize method for this. Therefore, create an alias for it,
  # but use one that is as unique to your tag as possible, optimally the tag's
  # name itself, so in this case:
  alias kana_initialize initialize
  def initialize
    # Call the old initialize method
    kana_initialize
    # Now initialize the kana font name
    self.kana_font_name = nil
    # This will call the method created above. Note that this is neither the
    # same as kana_font_name = nil (which would create a local variable), not as
    # @kana_font_name = nil (which would actually set it to nil, but we want it
    # to be reset to default).
  end
end



#
# Ruby/Romkan - a Romaji <-> Kana conversion library for Ruby.
#
# Copyright (C) 2001 Satoru Takabayashi <satoru@namazu.org>
#     All rights reserved.
#     This is free software with ABSOLUTELY NO WARRANTY.
#
# You can redistribute it and/or modify it under the terms of
# the Ruby's licence.
#
# NOTE: Ruby/Romkan can work only with EUC_JP encoding. ($KCODE="e")
#
# NOTE (Hermes author): The above note isn't true, at least not for Romaji ->
#     Kana conversion. I modified the original Ruby/Romkan library by simply
#     running them through a little PHP script which converted it from EUC_JP
#     to Unicode, and it works, as far as I can tell. I'm not sure whether
#     Kana -> Romaji would also work, as I've removed it, but it should. Also,
#     I've added Romaji to Katakana conversion (UPPER CASE LETTERS will be
#     translated to Katakana, lower case ones to Hiragana).
#     I've removed all the constants and methods that aren't used in the code
#     above, which is all of them except KUNREITAB, HEBBURNTAB,
#     normalize_double_n and to_kana, and then added punctuation and space
#     conversion. For the full version (though without the latter), see
#     Takabayashi-san no webusaito at http://0xcc.net/ruby-romkan/
#

module Romkan
  VERSION = '0.4'
end

class Array
  def pairs(s=2)
    0.step(self.size-1,s){
      |x| yield self.slice(x,s)
    }
  end
end

class String
# This table is imported from KAKASI <http://kakasi.namazu.org/> and modified.
  KUNREITAB = "\
ぁ	xa	あ	a	ぃ	xi	い	i	ぅ	xu
う	u	う゛	vu	う゛ぁ	va	う゛ぃ	vi 	う゛ぇ	ve
う゛ぉ	vo	ぇ	xe	え	e	ぉ	xo	お	o

か	ka	が	ga	き	ki	きゃ	kya	きゅ	kyu
きょ	kyo	ぎ	gi	ぎゃ	gya	ぎゅ	gyu	ぎょ	gyo
く	ku	ぐ	gu	け	ke	げ	ge	こ	ko
ご	go

さ	sa	ざ	za	し	si	しゃ	sya	しゅ	syu
しょ	syo	じ	zi	じゃ	zya	じゅ	zyu	じょ	zyo
す	su	ず	zu	せ	se	ぜ	ze	そ	so
ぞ	zo

た	ta	だ	da	ち	ti	ちゃ	tya	ちゅ	tyu
ちょ	tyo	ぢ	di	ぢゃ	dya	ぢゅ	dyu	ぢょ	dyo

っ	xtu
っう゛	vvu	っう゛ぁ	vva	っう゛ぃ	vvi
っう゛ぇ	vve	っう゛ぉ	vvo
っか	kka	っが	gga	っき	kki	っきゃ	kkya
っきゅ	kkyu	っきょ	kkyo	っぎ	ggi	っぎゃ	ggya
っぎゅ	ggyu	っぎょ	ggyo	っく	kku	っぐ	ggu
っけ	kke	っげ	gge	っこ	kko	っご	ggo	っさ	ssa
っざ	zza	っし	ssi	っしゃ	ssya
っしゅ	ssyu	っしょ	ssho
っじ	zzi	っじゃ	zzya	っじゅ	zzyu	っじょ	zzyo
っす	ssu	っず	zzu	っせ	sse	っぜ	zze	っそ	sso
っぞ	zzo	った	tta	っだ	dda	っち	tti
っちゃ	ttya	っちゅ	ttyu	っちょ	ttyo	っぢ	ddi
っぢゃ	ddya	っぢゅ	ddyu	っぢょ	ddyo	っつ	ttu
っづ	ddu	って	tte	っで	dde	っと	tto	っど	ddo
っは	hha	っば	bba	っぱ	ppa	っひ	hhi
っひゃ	hhya	っひゅ	hhyu	っひょ	hhyo	っび	bbi
っびゃ	bbya	っびゅ	bbyu	っびょ	bbyo	っぴ	ppi
っぴゃ	ppya	っぴゅ	ppyu	っぴょ	ppyo	っふ	hhu
っふぁ	ffa	っふぃ	ffi	っふぇ	ffe	っふぉ	ffo
っぶ	bbu	っぷ	ppu	っへ	hhe	っべ	bbe	っぺ    ppe
っほ	hho	っぼ	bbo	っぽ	ppo	っや	yya	っゆ	yyu
っよ	yyo	っら	rra	っり	rri	っりゃ	rrya
っりゅ	rryu	っりょ	rryo	っる	rru	っれ	rre
っろ	rro

つ	tu	づ	du	て	te	で	de	と	to
ど	do

な	na	に	ni	にゃ	nya	にゅ	nyu	にょ	nyo
ぬ	nu	ね	ne	の	no

は	ha	ば	ba	ぱ	pa	ひ	hi	ひゃ	hya
ひゅ	hyu	ひょ	hyo	び	bi	びゃ	bya	びゅ	byu
びょ	byo	ぴ	pi	ぴゃ	pya	ぴゅ	pyu	ぴょ	pyo
ふ	hu	ふぁ	fa	ふぃ	fi	ふぇ	fe	ふぉ	fo
ぶ	bu	ぷ	pu	へ	he	べ	be	ぺ	pe
ほ	ho	ぼ	bo	ぽ	po

ま	ma	み	mi	みゃ	mya	みゅ	myu	みょ	myo
む	mu	め	me	も	mo

ゃ	xya	や	ya	ゅ	xyu	ゆ	yu	ょ	xyo
よ	yo

ら	ra	り	ri	りゃ	rya	りゅ	ryu	りょ	ryo
る	ru	れ	re	ろ	ro

ゎ	xwa	わ	wa	ゐ	wi	ゑ	we
を	wo	ん	n

ん     n'
でぃ   dyi
ー     -
ちぇ    tye
っちぇ	ttye
じぇ	zye

ァ	XA	ア	A	ィ	XI	イ	I	ゥ	XU
ウ	U	ウ゛	VU	ウ゛ァ	VA	ウ゛ィ	VI 	ウ゛ェ	VE
ウ゛ォ	VO	ェ	XE	エ	E	ォ	XO	オ	O

カ	KA	ガ	GA	キ	KI	キャ	KYA	キュ	KYU
キョ	KYO	ギ	GI	ギャ	GYA	ギュ	GYU	ギョ	GYO
ク	KU	グ	GU	ケ	KE	ゲ	GE	コ	KO
ゴ	GO

サ	SA	ザ	ZA	シ	SI	シャ	SYA	シュ	SYU
ショ	SYO	ジ	ZI	ジャ	ZYA	ジュ	ZYU	ジョ	ZYO
ス	SU	ズ	ZU	セ	SE	ゼ	ZE	ソ	SO
ゾ	ZO

タ	TA	ダ	DA	チ	TI	チャ	TYA	チュ	TYU
チョ	TYO	ヂ	DI	ヂャ	DYA	ヂュ	DYU	ヂョ	DYO

ッ	XTU
ッウ゛	VVU	ッウ゛ァ	VVA	ッウ゛ィ	VVI
ッウ゛ェ	VVE	ッウ゛ォ	VVO
ッカ	KKA	ッガ	GGA	ッキ	KKI	ッキャ	KKYA
ッキュ	KKYU	ッキョ	KKYO	ッギ	GGI	ッギャ	GGYA
ッギュ	GGYU	ッギョ	GGYO	ック	KKU	ッグ	GGU
ッケ	KKE	ッゲ	GGE	ッコ	KKO	ッゴ	GGO	ッサ	SSA
ッザ	ZZA	ッシ	SSI	ッシャ	SSYA
ッシュ	SSYU	ッショ	SSHO
ッジ	ZZI	ッジャ	ZZYA	ッジュ	ZZYU	ッジョ	ZZYO
ッス	SSU	ッズ	ZZU	ッセ	SSE	ッゼ	ZZE	ッソ	SSO
ッゾ	ZZO	ッタ	TTA	ッダ	DDA	ッチ	TTI
ッチャ	TTYA	ッチュ	TTYU	ッチョ	TTYO	ッヂ	DDI
ッヂャ	DDYA	ッヂュ	DDYU	ッヂョ	DDYO	ッツ	TTU
ッヅ	DDU	ッテ	TTE	ッデ	DDE	ット	TTO	ッド	DDO
ッハ	HHA	ッバ	BBA	ッパ	PPA	ッヒ	HHI
ッヒャ	HHYA	ッヒュ	HHYU	ッヒョ	HHYO	ッビ	BBI
ッビャ	BBYA	ッビュ	BBYU	ッビョ	BBYO	ッピ	PPI
ッピャ	PPYA	ッピュ	PPYU	ッピョ	PPYO	ッフ	HHU
ッファ	FFA	ッフィ	FFI	ッフェ	FFE	ッフォ	FFO
ッブ	BBU	ップ	PPU	ッヘ	HHE	ッベ	BBE	ッペ    PPE
ッホ	HHO	ッボ	BBO	ッポ	PPO	ッヤ	YYA	ッユ	YYU
ッヨ	YYO	ッラ	RRA	ッリ	RRI	ッリャ	RRYA
ッリュ	RRYU	ッリョ	RRYO	ッル	RRU	ッレ	RRE
ッロ	RRO

ツ	TU	ヅ	DU	テ	TE	デ	DE	ト	TO
ド	DO

ナ	NA	ニ	NI	ニャ	NYA	ニュ	NYU	ニョ	NYO
ヌ	NU	ネ	NE	ノ	NO

ハ	HA	バ	BA	パ	PA	ヒ	HI	ヒャ	HYA
ヒュ	HYU	ヒョ	HYO	ビ	BI	ビャ	BYA	ビュ	BYU
ビョ	BYO	ピ	PI	ピャ	PYA	ピュ	PYU	ピョ	PYO
フ	HU	ファ	FA	フィ	FI	フェ	FE	フォ	FO
ブ	BU	プ	PU	ヘ	HE	ベ	BE	ペ	PE
ホ	HO	ボ	BO	ポ	PO

マ	MA	ミ	MI	ミャ	MYA	ミュ	MYU	ミョ	MYO
ム	MU	メ	ME	モ	MO

ャ	XYA	ヤ	YA	ュ	XYU	ユ	YU	ョ	XYO
ヨ	YO

ラ	RA	リ	RI	リャ	RYA	リュ	RYU	リョ	RYO
ル	RU	レ	RE	ロ	RO

ヮ	XWA	ワ	WA	ヰ	WI	ヱ	WE
ヲ	WO	ン	N

ン     N'
ディ   DYI
ー     -
チェ    TYE
ッチェ	TTYE
ジェ	ZYE
"

  HEPBURNTAB = "\
ぁ	xa	あ	a	ぃ	xi	い	i	ぅ	xu
う	u	う゛	vu	う゛ぁ	va	う゛ぃ	vi	う゛ぇ	ve
う゛ぉ	vo	ぇ	xe	え	e	ぉ	xo	お	o


か	ka	が	ga	き	ki	きゃ	kya	きゅ	kyu
きょ	kyo	ぎ	gi	ぎゃ	gya	ぎゅ	gyu	ぎょ	gyo
く	ku	ぐ	gu	け	ke	げ	ge	こ	ko
ご	go

さ	sa	ざ	za	し	shi	しゃ	sha	しゅ	shu
しょ	sho	じ	ji	じゃ	ja	じゅ	ju	じょ	jo
す	su	ず	zu	せ	se	ぜ	ze	そ	so
ぞ	zo

た	ta	だ	da	ち	chi	ちゃ	cha	ちゅ	chu
ちょ	cho	ぢ	di	ぢゃ	dya	ぢゅ	dyu	ぢょ	dyo

っ	xtsu
っう゛	vvu	っう゛ぁ	vva	っう゛ぃ	vvi
っう゛ぇ	vve	っう゛ぉ	vvo
っか	kka	っが	gga	っき	kki	っきゃ	kkya
っきゅ	kkyu	っきょ	kkyo	っぎ	ggi	っぎゃ	ggya
っぎゅ	ggyu	っぎょ	ggyo	っく	kku	っぐ	ggu
っけ	kke	っげ	gge	っこ	kko	っご	ggo	っさ	ssa
っざ	zza	っし	sshi	っしゃ	ssha
っしゅ	sshu	っしょ	ssho
っじ	jji	っじゃ	jja	っじゅ	jju	っじょ	jjo
っす	ssu	っず	zzu	っせ	sse	っぜ	zze	っそ	sso
っぞ	zzo	った	tta	っだ	dda	っち	cchi
っちゃ	ccha	っちゅ	cchu	っちょ	ccho	っぢ	ddi
っぢゃ	ddya	っぢゅ	ddyu	っぢょ	ddyo	っつ	ttsu
っづ	ddu	って	tte	っで	dde	っと	tto	っど	ddo
っは	hha	っば	bba	っぱ	ppa	っひ	hhi
っひゃ	hhya	っひゅ	hhyu	っひょ	hhyo	っび	bbi
っびゃ	bbya	っびゅ	bbyu	っびょ	bbyo	っぴ	ppi
っぴゃ	ppya	っぴゅ	ppyu	っぴょ	ppyo	っふ	ffu
っふぁ	ffa	っふぃ	ffi	っふぇ	ffe	っふぉ	ffo
っぶ	bbu	っぷ	ppu	っへ	hhe	っべ	bbe	っぺ	ppe
っほ	hho	っぼ	bbo	っぽ	ppo	っや	yya	っゆ	yyu
っよ	yyo	っら	rra	っり	rri	っりゃ	rrya
っりゅ	rryu	っりょ	rryo	っる	rru	っれ	rre
っろ	rro

つ	tsu	づ	du	て	te	で	de	と	to
ど	do

な	na	に	ni	にゃ	nya	にゅ	nyu	にょ	nyo
ぬ	nu	ね	ne	の	no

は	ha	ば	ba	ぱ	pa	ひ	hi	ひゃ	hya
ひゅ	hyu	ひょ	hyo	び	bi	びゃ	bya	びゅ	byu
びょ	byo	ぴ	pi	ぴゃ	pya	ぴゅ	pyu	ぴょ	pyo
ふ	fu	ふぁ	fa	ふぃ	fi	ふぇ	fe	ふぉ	fo
ぶ	bu	ぷ	pu	へ	he	べ	be	ぺ	pe
ほ	ho	ぼ	bo	ぽ	po

ま	ma	み	mi	みゃ	mya	みゅ	myu	みょ	myo
む	mu	め	me	も	mo

ゃ	xya	や	ya	ゅ	xyu	ゆ	yu	ょ	xyo
よ	yo

ら	ra	り	ri	りゃ	rya	りゅ	ryu	りょ	ryo
る	ru	れ	re	ろ	ro

ゎ	xwa	わ	wa	ゐ	wi	ゑ	we
を	wo	ん	n

ん     n'
でぃ   dyi
ー     -
ちぇ    che
っちぇ	cche
じぇ	je

ァ	XA	ア	A	ィ	XI	イ	I	ゥ	XU
ウ	U	ウ゛	VU	ウ゛ァ	VA	ウ゛ィ	VI	ウ゛ェ	VE
ウ゛ォ	VO	ェ	XE	エ	E	ォ	XO	オ	O


カ	KA	ガ	GA	キ	KI	キャ	KYA	キュ	KYU
キョ	KYO	ギ	GI	ギャ	GYA	ギュ	GYU	ギョ	GYO
ク	KU	グ	GU	ケ	KE	ゲ	GE	コ	KO
ゴ	GO

サ	SA	ザ	ZA	シ	SHI	シャ	SHA	シュ	SHU
ショ	SHO	ジ	JI	ジャ	JA	ジュ	JU	ジョ	JO
ス	SU	ズ	ZU	セ	SE	ゼ	ZE	ソ	SO
ゾ	ZO

タ	TA	ダ	DA	チ	CHI	チャ	CHA	チュ	CHU
チョ	CHO	ヂ	DI	ヂャ	DYA	ヂュ	DYU	ヂョ	DYO

ッ	XTSU
ッウ゛	VVU	ッウ゛ァ	VVA	ッウ゛ィ	VVI
ッウ゛ェ	VVE	ッウ゛ォ	VVO
ッカ	KKA	ッガ	GGA	ッキ	KKI	ッキャ	KKYA
ッキュ	KKYU	ッキョ	KKYO	ッギ	GGI	ッギャ	GGYA
ッギュ	GGYU	ッギョ	GGYO	ック	KKU	ッグ	GGU
ッケ	KKE	ッゲ	GGE	ッコ	KKO	ッゴ	GGO	ッサ	SSA
ッザ	ZZA	ッシ	SSHI	ッシャ	SSHA
ッシュ	SSHU	ッショ	SSHO
ッジ	JJI	ッジャ	JJA	ッジュ	JJU	ッジョ	JJO
ッス	SSU	ッズ	ZZU	ッセ	SSE	ッゼ	ZZE	ッソ	SSO
ッゾ	ZZO	ッタ	TTA	ッダ	DDA	ッチ	CCHI
ッチャ	CCHA	ッチュ	CCHU	ッチョ	CCHO	ッヂ	DDI
ッヂャ	DDYA	ッヂュ	DDYU	ッヂョ	DDYO	ッツ	TTSU
ッヅ	DDU	ッテ	TTE	ッデ	DDE	ット	TTO	ッド	DDO
ッハ	HHA	ッバ	BBA	ッパ	PPA	ッヒ	HHI
ッヒャ	HHYA	ッヒュ	HHYU	ッヒョ	HHYO	ッビ	BBI
ッビャ	BBYA	ッビュ	BBYU	ッビョ	BBYO	ッピ	PPI
ッピャ	PPYA	ッピュ	PPYU	ッピョ	PPYO	ッフ	FFU
ッファ	FFA	ッフィ	FFI	ッフェ	FFE	ッフォ	FFO
ッブ	BBU	ップ	PPU	ッヘ	HHE	ッベ	BBE	ッペ	PPE
ッホ	HHO	ッボ	BBO	ッポ	PPO	ッヤ	YYA	ッユ	YYU
ッヨ	YYO	ッラ	RRA	ッリ	RRI	ッリャ	RRYA
ッリュ	RRYU	ッリョ	RRYO	ッル	RRU	ッレ	RRE
ッロ	RRO

ツ	TSU	ヅ	DU	テ	TE	デ	DE	ト	TO
ド	DO

ナ	NA	ニ	NI	ニャ	NYA	ニュ	NYU	ニョ	NYO
ヌ	NU	ネ	NE	ノ	NO

ハ	HA	バ	BA	パ	PA	ヒ	HI	ヒャ	HYA
ヒュ	HYU	ヒョ	HYO	ビ	BI	ビャ	BYA	ビュ	BYU
ビョ	BYO	ピ	PI	ピャ	PYA	ピュ	PYU	ピョ	PYO
フ	FU	ファ	FA	フィ	FI	フェ	FE	フォ	FO
ブ	BU	プ	PU	ヘ	HE	ベ	BE	ペ	PE
ホ	HO	ボ	BO	ポ	PO

マ	MA	ミ	MI	ミャ	MYA	ミュ	MYU	ミョ	MYO
ム	MU	メ	ME	モ	MO

ャ	XYA	ヤ	YA	ュ	XYU	ユ	YU	ョ	XYO
ヨ	YO

ラ	RA	リ	RI	リャ	RYA	リュ	RYU	リョ	RYO
ル	RU	レ	RE	ロ	RO

ヮ	XWA	ワ	WA	ヰ	WI	ヱ	WE
ヲ	WO	ン	N

ン     N'
ディ   DYI
ー     -
チェ    CHE
ッチェ	CCHE
ジェ	JE
"

  PUNCTUATION = "\
、	,	。	.
「	[	」	]	・	/
"

  ROMKAN = (romakana = Hash.new
	    (KUNREITAB + HEPBURNTAB + PUNCTUATION).split(/\s+/).pairs {|x|
	      kana, roma = x
	      romakana[roma] = kana
	    }
	    romakana[" "] = "　"
	    romakana)

  # Sort in long order so that a longer Romaji sequence precedes.
  ROMPAT = (rompat = ROMKAN.keys.sort{|a, b| b.length <=> a.length}.join("|")
      rompat.gsub(/([,.\[\]\/ ])/){"\\#{$1}"})

  # FIXME: ad hod solution
  # tanni   => tan'i
  # kannji  => kanji
  # hannnou => han'nou
  # hannnya => han'nya
  def normalize_double_n
    self.gsub(/nn/, "n'").gsub(/n\'(?=[^aiueoyn]|$)/, "n")
  end

  # Romaji -> Kana
  # It can handle both Hepburn and Kunrei sequences.
  def to_kana
    tmp = self.normalize_double_n
    tmp.gsub(/(#{ROMPAT})/) { ROMKAN[$1] }
  end
end